#ifndef ZCURVE_H
#define ZCURVE_H

#include <inttypes.h>
#include <bitset>
#include <array>

#include "../persistencia/serializable.h"
#include "../persistencia/binarystream.h"

template<size_t D>
class Point : public std::array<uint32_t, D>{
    public:
        Point() : std::array<uint32_t, D>() {}
        Point(std::initializer_list<uint32_t> params) : std::array<uint32_t, D>(){
            uint8_t i = 0;
            for (uint32_t element : params)
                (*this)[i++] = element;
        }
};

template<size_t N>
class ZPoint : public std::bitset<N>, public Serializable {
    public:
        ZPoint(){}
        ZPoint(unsigned long val) : std::bitset<N>(val) {}

        virtual void read(BinaryStream& in);
        virtual void write(BinaryStream& out);
        virtual std::string toString();

        ZPoint<N>& operator++();

        bool operator<(const ZPoint<N>& that) const { return this->compare(that) < 0; }
        bool operator>(const ZPoint<N>& that) const { return this->compare(that) > 0; }
        bool operator<=(const ZPoint<N>& that) const { return this->compare(that) <= 0; }
        bool operator>=(const ZPoint<N>& that) const { return this->compare(that) >= 0; }

        int8_t compare(const ZPoint<N>& that) const;
};

template <size_t D, size_t K> //D: dimensiones, K: bits de cada subclave
class ZCurve {
    public:
        ZPoint<D*K> map(Point<D> point);

        Point<D> inv(ZPoint<D*K> zPoint);

        bool inRange(ZPoint<D*K> zMin, ZPoint<D*K> zMax, ZPoint<D*K> zPoint);

        ZPoint<D*K> nextZAddress(ZPoint<D*K> zMin, ZPoint<D*K> zMax, ZPoint<D*K> zCur);
};

#endif // ZCURVE_H
